#pragma once

#include <AVUIDependencyProperty.h>

namespace AVUI {

/// <summary>Represents the base class for value setters. </summary>
class SetterBase : public Object
{
public:
    DECLARE_ELEMENT(SetterBase, Object);

    /// <summary>Gets a value that indicates whether this object is in an immutable state.</summary>
    /// <returns>true if this object is in an immutable state; otherwise, false.</returns>
    bool get_IsSealed() { return m_sealed; }

protected:
    /// <summary>Checks whether this object is read-only and cannot be changed.</summary>
    /// <returns />
    void CheckSealed() { if(m_sealed) LibraryThrowException(object_allocate<InvalidOperationException>("Cannot change setter after being sealed.")); }

public:
    SetterBase() : m_sealed(false) { }
    virtual void Seal() { m_sealed = true; }

private:
    bool m_sealed;
};


/// <summary>Initializes a new instance of the <see cref="T:System.Windows.Setter" /> class.</summary>
class Setter : public SetterBase
{
    friend class XamlParser;
public:
    DECLARE_ELEMENT(Setter, SetterBase);

#pragma region Public Methods

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Setter" /> class.</summary>
    Setter();

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Setter" /> class with the specified property and value.</summary>
    /// <param name="property">The <see cref="T:System.Windows.DependencyProperty" /> to apply the <see cref="P:System.Windows.Setter.Value" /> to.</param>
    /// <param name="value">The value to apply to the property.</param>
    Setter(DependencyProperty* pProperty, Object* pValue);

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Setter" /> class with the specified property, value, and target name.</summary>
    /// <param name="property">The <see cref="T:System.Windows.DependencyProperty" /> to apply the <see cref="P:System.Windows.Setter.Value" /> to.</param>
    /// <param name="value">The value to apply to the property.</param>
    /// <param name="targetName">The name of the child node this <see cref="T:System.Windows.Setter" /> is intended for.</param>
    Setter(DependencyProperty* pProperty, Object* pValue, String* pTargetName);

#pragma endregion Public Methods
 
#pragma region Public Properties

    /// <summary>Gets or sets the property to which the <see cref="P:System.Windows.Setter.Value" /> will be applied.</summary>
    /// <returns>A <see cref="T:System.Windows.DependencyProperty" /> to which the <see cref="P:System.Windows.Setter.Value" /> will be applied. The default value is null.</returns>
    /// <exception cref="T:System.ArgumentNullException">The <see cref="P:System.Windows.Setter.Property" /> property cannot be null.</exception>
    /// <exception cref="T:System.ArgumentException">The specified <see cref="P:System.Windows.Setter.Property" /> property cannot be read-only.</exception>
    /// <exception cref="T:System.InvalidOperationException">If the specified <see cref="P:System.Windows.Setter.Value" /> is not valid for the type of the specified <see cref="P:System.Windows.Setter.Property" />.</exception>
    DependencyProperty* get_Property() { return m_pProperty; }
    void set_Property(DependencyProperty* pProperty) { CheckValidProperty(pProperty); CheckSealed(); m_pProperty = pProperty; }
    DECLARE_PROPERTY(Property, DependencyProperty, false)

    /// <summary>Gets or sets the name of the object this <see cref="T:System.Windows.Setter" /> is intended for.</summary>
    /// <returns>The default value is null.</returns>
    String* get_TargetName() { return m_pTarget; }
    void set_TargetName(String* pValue) { CheckSealed(); m_pTarget = pValue; }
    DECLARE_PROPERTY(TargetName, String, false);

    /// <summary>Gets or sets the value to apply to the property that is specified by this <see cref="T:System.Windows.Setter" />.</summary>
    /// <returns>The default value is <see cref="F:System.Windows.DependencyProperty.UnsetValue" />.</returns>
    /// <exception cref="T:System.ArgumentException">If the specified <see cref="P:System.Windows.Setter.Value" /> is set to <see cref="F:System.Windows.DependencyProperty.UnsetValue" />.</exception>
    Object* get_Value();
    void set_Value(Object* pValue);
    DECLARE_PROPERTY_BEGIN(Value, Object, false)
        DECLARE_ATTRIBUTE_DEPENDSON(TargetName)
        DECLARE_ATTRIBUTE_DEPENDSON(Property)
    DECLARE_PROPERTY_END()

#pragma endregion Public Properties

public:

#pragma region Internal Methods

    virtual void Seal() AVUI_OVERRIDE;

#pragma endregion Internal Methods

#pragma region Internal Properties

    Object* get_ValueInternal() { return m_pValue; }

#pragma endregion Internal Properties

private:

#pragma region Private Methods

    void CheckValidProperty(DependencyProperty* pProperty);
    void Initialize(DependencyProperty* pProperty, Object* pValue, String* pTarget);

#pragma endregion Private Methods

#pragma region Private Fields

    DependencyProperty* m_pProperty;
    TRefCountedPtr<String> m_pTarget;
    TRefCountedPtr<Object> m_pValue;

#pragma endregion Private Fields

};

/// <summary>Represents an event setter in a style. Event setters invoke the specified event handlers in response to events.</summary>
class EventSetter : public SetterBase
{
public:
    DECLARE_ELEMENT(EventSetter, SetterBase);

#pragma region Public Methods

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.EventSetter" /> class. </summary>
    EventSetter() : m_pEvent(NULL), m_handledEventsToo(false) { };

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.EventSetter" /> class, using the provided event and handler parameters. </summary>
    /// <param name="routedEvent">The particular routed event that the <see cref="T:System.Windows.EventSetter" /> responds to.</param>
    /// <param name="handler">The handler to assign in this setter.</param>
    EventSetter(RoutedEvent* pRoutedEvent, Delegate* pHandler);

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets or sets the particular routed event that this <see cref="T:System.Windows.EventSetter" /> responds to.</summary>
    /// <returns>The identifier field of the routed event.</returns>
    /// <exception cref="T:System.InvalidOperationException">Attempted to set this property on a sealed <see cref="T:System.Windows.EventSetter" /> .</exception>
    RoutedEvent* get_Event() { return m_pEvent; }
    void set_Event(RoutedEvent* pValue);
    DECLARE_PROPERTY(Event, RoutedEvent, false);

    /// <summary>Gets or sets a value that determines whether the handler assigned to the setter should still be invoked, even if the event is marked handled in its event data. </summary>
    /// <returns>true if the handler should still be invoked; otherwise, false.</returns>
    bool get_HandledEventsToo() { return m_handledEventsToo; }
    void set_HandledEventsToo(bool value) { CheckSealed(); m_handledEventsToo = value; }
    DECLARE_PROPERTY(HandledEventsToo, bool, false);

    /// <summary>Gets or sets the reference to a handler for a routed event in the setter. </summary>
    /// <returns>Reference to the handler that is attached by this <see cref="T:System.Windows.EventSetter" />.</returns>
    Delegate* get_Handler() { return m_pHandler; }
    void set_Handler(Delegate* pValue);
    DECLARE_PROPERTY(Handler, Delegate, false);

#pragma endregion Public Properties

public:

#pragma region Internal Methods

    virtual void Seal() AVUI_OVERRIDE;

#pragma endregion Internal Methods

private:

#pragma region Private Fields

    RoutedEvent* m_pEvent;
    bool m_handledEventsToo;
    TRefCountedPtr<Delegate> m_pHandler;

#pragma endregion Private Fields

};


}; // namespace AVUI
